tree grammar LocStatGen;

options {
	tokenVocab=LocStat;
	ASTLabelType=LocStatAST;
	output=template;
}

@header {
	package data.analysis.grammar;
	
	import java.io.ByteArrayInputStream;
	import java.io.IOException;
	import java.util.Map;
	import data.analysis.grammar.symbol.Scope;
	import script.grammar.FilterTranslator;
	import stream.ErrorStream;
}

@members {
	protected OutputDictionary dictionary;
	protected int nDefn = 0;
	
	protected FilterTranslator trans;
	protected Map<Scope,Class<?>> filterTypeMap;
	
	protected ErrorStream err;
	
	public LocStatGen(TreeNodeStream in, OutputDictionary dictionary, FilterTranslator trans,
			Map<Scope,Class<?>> filterTypeMap, ErrorStream err) {
		this(in);
		this.dictionary = dictionary;
		this.trans = trans;
		this.filterTypeMap = filterTypeMap;
		this.err = err;
	}
	
	protected String[] getFilterBody(LocStatAST string) {
		try {
			String raw = string.getText();
   			String targetCode = trans.buildTargetCode(new ByteArrayInputStream(raw.substring(1, raw.length() - 1).getBytes()),
   					filterTypeMap.get(string.scope), "", FilterTranslator.CODE_MODE_BODY);
   			if (targetCode == null) {
   				throw new IOException();
   			}
   			
   			return new String[] {targetCode, filterTypeMap.get(string.scope).getName()};
   		} catch (IOException e) {
   			this.err.write("Error: A problem occured while parsing custom filter code.");
   			throw new RuntimeException();
   		}
	}
}

start
	:	(s+=defn)+ -> defns(stats={$s})
	;
	
defn:	^(DEFINE STRING func) -> stat(func={$func.st}, name={$STRING.text}, id={""+nDefn++})
	;
	
func:	^(COUNT count) -> countFunc(body={$count.st})
	|	^(FRACTION a=count b=count) -> fractionFunc(num={$a.st}, denom={$b.st})
	|	^(MEAN ^(SUBJECT ID (s+=condBase)*) ^(SCOPE_OP count)) 
			-> meanFunc(type={dictionary.get($ID).type}, getter={dictionary.get($ID).getter}, conds={$s}, attr={$count.st})
	|	^(MEDIAN ^(SUBJECT ID (s+=condBase)*) ^(SCOPE_OP count)) -> medFunc(type={dictionary.get($ID).type}, getter={dictionary.get($ID).getter}, conds={$s}, attr={$count.st})
	;
	
count
	:	^(SUBJECT ID (s+=condBase)*) -> count(type={dictionary.get($ID).type}, getter={dictionary.get($ID).getter}, conds={$s})
	;
	
condBase
@init{boolean not = false;}
	:	^(COND (COND_DEFAULT {not = false;}|NOT {not = true;}) ^(SUBJECT ID)) -> condBase(body={%cond(not={not}, bool={dictionary.get($ID).getter})})
	|	^(COND numOp ^(SUBJECT ID (s+=condBase)*)) -> condBase(body={%numCond(type={dictionary.get($ID).type}, getter={dictionary.get($ID).getter}, conds={$s}, numOp={$numOp.st})})
	|	^(COND NOT a=condBase) -> condBase(body={%cond(not={true}, bool={$a.st})})
	|	^(COND SCRIPT STRING) {String[] filterArgs = getFilterBody($STRING);} 
			-> condBase(body={%script(typeName={filterArgs[1]}, body={filterArgs[0]})})
	;
	
numOp
	:	EXACTLY_ONE {$st=%{"n == 1"};}
	|	ONE_OR_MORE {$st=%{"n > 0"};}
	;